﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;

namespace FerrSimplicity
{
    public class Util
    {
        public static object Clone(object aObject)
        {
            //Activator.CreateInstance(
            object      newObject = Activator.CreateInstance(aObject.GetType(), true);
            FieldInfo[] fields    = newObject.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
            int         i         = 0;

            foreach (FieldInfo fi in aObject.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy))
            {
                // if it implements ICloneable, then our job is easy, we just pass it on to that
                Type ICloneType = fi.FieldType.GetInterface("ICloneable", true);
                if (ICloneType != null)
                {
                    ICloneable IClone = (ICloneable)fi.GetValue(aObject);
                    if (IClone != null)
                        fields[i].SetValue(newObject, IClone.Clone());
                }
                else
                {
                    fields[i].SetValue(newObject, fi.GetValue(aObject));
                }

                // now, if it's a list that we're cloning, we have to go through, and clone all the items
                Type IEnumerableType = fi.FieldType.GetInterface("IEnumerable", true);
                if (IEnumerableType != null)
                {
                    IEnumerable IEnum = (IEnumerable)fi.GetValue(aObject);

                    Type IListType = fields[i].FieldType.GetInterface("IList", true);
                    Type IDicType  = fields[i].FieldType.GetInterface("IDictionary", true);

                    int j = 0;
                    if (IListType != null)
                    {
                        IList list = (IList)fields[i].GetValue(newObject);

                        foreach (object obj in IEnum)
                        {
                            ICloneType = obj.GetType().GetInterface("ICloneable", true);

                            if (ICloneType != null)
                            {
                                ICloneable clone = (ICloneable)obj;

                                list[j] = clone.Clone();
                            }
                            else
                            {
                                list[j] = Util.Clone(obj);
                            }

                            j++;
                        }
                    }
                    else if (IDicType != null)
                    {
                        IDictionary dic = (IDictionary)fields[i].GetValue(newObject);
                        j = 0;

                        foreach (DictionaryEntry de in IEnum)
                        {
                            ICloneType = de.Value.GetType().GetInterface("ICloneable", true);

                            if (ICloneType != null)
                            {
                                ICloneable clone = (ICloneable)de.Value;

                                dic[de.Key] = clone.Clone();
                            }
                            else
                            {
                                dic[de.Key] = Util.Clone(de.Value);
                            }
                            j++;
                        }
                    }
                }
                i++;
            }
            return newObject;
        }
    }
}
